﻿using MudBlazor.Utilities;
using System.Reflection;

namespace MXERP.Blazor.Components
{
    [CascadingTypeParameter(nameof(T))]
    public partial class Table<T> : MudComponentBase, IAsyncDisposable
    {
        [Parameter]
        public EventCallback<T> SelectedItemChanged { get; set; }
        [Parameter]
        public EventCallback<HashSet<T>> SelectedItemsChanged { get; set; }

        private IEnumerable<T>? _items;
        [Parameter]
        public IEnumerable<T>? Items
        {
            get => _items;
            set
            {
                if (_items == value)
                    return;
                _items = value;
            }
        }

        [Parameter]
        public Func<T, IEnumerable<T>> TreeChildren { get; set; } = _ => Enumerable.Empty<T>();

        [Parameter]
        public string? Height { get; set; }

        [Parameter]
        public int OverscanCount { get; set; } = 3;

        [Parameter]
        public float ItemSize { get; set; } = 50f;

        [Parameter]
        public int IndentSize { get; set; } = 15;

        //[Parameter]
        //public EventCallback<RowData<T>> OnRowClick { get; set; }

        [Parameter]
        public bool DefaultExpandAllRows { get; set; }

        [Parameter]
        public int DefaultExpandMaxLevel { get; set; } = 4;

        [Parameter]
        public bool Virtualize { get; set; }

        [Parameter]
        public RenderFragment? Columns { get; set; }

        private T? _selectedItem;
        [Parameter]
        public T? SelectedItem
        {
            get => _selectedItem;
            set
            {
                if (EqualityComparer<T>.Default.Equals(SelectedItem, value))
                    return;
                _selectedItem = value;
                SelectedItemChanged.InvokeAsync(value);
            }
        }

        [Parameter]
        public bool MultiSelection { get; set; }
        [Parameter]
        public HashSet<T>? SelectedItems
        {
            get
            {
                if (!MultiSelection)
                    if (_selectedItem is null)
                        return new HashSet<T>(Array.Empty<T>());
                    else
                        return new HashSet<T>(new T[] { _selectedItem });
                else
                    return Selection;
            }
            set
            {
                if (value == Selection)
                    return;
                if (value == null)
                {
                    if (Selection.Count == 0)
                        return;
                    Selection = new HashSet<T>();
                }
                else
                    Selection = value;
                SelectedItemsChanged.InvokeAsync(Selection);
            }
        }

        [Parameter]
        public int Elevation { set; get; } = 1;

        [Parameter]
        public bool Square { get; set; }

        [Parameter]
        public bool Outlined { get; set; }

        [Parameter]
        public bool Bordered { get; set; }

        [Parameter]
        public bool Dense { get; set; }

        [Parameter]
        public bool Hover { get; set; }

        [Parameter]
        public bool Striped { get; set; }


        private PropertyInfo[] _properties = typeof(T).GetProperties();

        public readonly List<Column<T>> RenderedColumns = new List<Column<T>>();

        public readonly List<TableRow<T>> RenderedRows = new List<TableRow<T>>();

        private MudVirtualize<T>? _mudVirtualize;

        internal ItemsProviderDelegate<T>? VirtualItemsProvider { get; set; }

        public HashSet<T> Selection { get; set; } = new HashSet<T>();

        public Column<T>? ShowExpandedColumn { get; set; }

        internal SelectColumn<T>? SelectColumn { get; set; }

        protected string ClassName =>
            new CssBuilder("md-table")
               .AddClass("md-table-dense", Dense)
               .AddClass("md-table-hover", Hover)
               .AddClass("md-table-bordered", Bordered)
               .AddClass("md-table-striped", Striped)
               .AddClass("md-table-outlined", Outlined)
               .AddClass("md-table-square", Square)
               .AddClass($"md-elevation-{Elevation}", !Outlined)
              .AddClass(Class)
            .Build();

        protected string _style =>
            new StyleBuilder()
                //.AddStyle("overflow-x", "auto")
                //.AddStyle("position", "relative", when: hasStickyColumns)
                .AddStyle(Style)
            .Build();

        protected string TableClassName =>
            new CssBuilder("md-table-container")
            .Build();

        protected string TableStyle =>
            new StyleBuilder()
                .AddStyle("height", Height, !string.IsNullOrWhiteSpace(Height))
            .Build();

        protected string HeadClassName =>
            new CssBuilder("md-table-head")
           .Build();

        protected string BodyClassName =>
            new CssBuilder("md-table-body")
           .Build();

        private ElementReference _tableElement;

        internal void AddColumn(Column<T> column)
        {
            if (column.Tag?.ToString() == "select-column")
            {
                RenderedColumns.Insert(0, column);
            }
            else
            {
                RenderedColumns.Add(column);
                ShowExpandedColumn ??= column;
            }
        }

        internal void AddRow(TableRow<T> row)
        {
            RenderedRows.Add(row);
        }

        internal void RemoveColumn(Column<T> column)
        {
            RenderedColumns.Remove(column);
        }

        internal void RemoveRow(TableRow<T> row)
        {
            RenderedRows.Remove(row);
        }

        internal async Task SetSelectedItem(bool value, T item)
        {
            if (value)
            {
                Selection.Add(item);
                SelectedItem = item;
            }
            else
            {
                Selection.Remove(item);
                if (item == null || item.Equals(SelectedItem))
                {
                    SelectedItem = default;
                }
            }
            await SelectedItemsChanged.InvokeAsync(Selection);
        }

        internal async Task SetSelectAll(bool value)
        {
            var items = RenderedRows.Select(s => s.Item).ToList();
            if (value)
                Selection = new HashSet<T>(items);
            else
                Selection.Clear();
            await SelectedItemsChanged.InvokeAsync(Selection);
        }

        public async ValueTask DisposeAsync()
        {
            await JsRuntime.InvokeVoidAsyncWithErrorHandling("mdTable.cancelTableScrollEvent", _tableElement);
            GC.SuppressFinalize(this);
        }

        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            await base.OnAfterRenderAsync(firstRender);
            if (firstRender)
            {
                await JsRuntime.InvokeVoidAsyncWithErrorHandling("mdTable.registerTableScrollEvent", _tableElement);
            }
        }
    }
}
